package androidawn.framework.binding;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import androidawn.framework.model.IPropertyChangedListener;
import androidawn.framework.model.IPropertyChangedNotify;

public class ChangeWatcher {
	private List<IChangeWatcherListener> listeners;
	
	private IPropertyChangedNotify source;
	
	private String path;
	
	private WeakReference<IPropertyChangedNotify> ref;
	
	private HashMap<IPropertyChangedNotify, IPropertyChangedListener> watchListeners;
	
	public ChangeWatcher(IPropertyChangedNotify source, String path, boolean useWeekReference)
	{
		listeners = new ArrayList<IChangeWatcherListener>();
		watchListeners = new HashMap<IPropertyChangedNotify, IPropertyChangedListener>();
		this.path = path;
		if(useWeekReference)
		{
			ref = new WeakReference<IPropertyChangedNotify>(source);
		}
		else
		{
			this.source = source;
		}
	}
	
	public boolean watch()
	{
		String[] items = path.split("\\.");
		Object target = getObject();
		boolean success = true;
		for(String path : items)
		{
			if(target instanceof IPropertyChangedNotify)
			{
				watchObjectProperty((IPropertyChangedNotify)target,path);
			}
			else
			{
				success = false;
				break;
			}
			target = BindingUitl.getValue(target, path);
		}
		if(!success)
		{
			stop();
		}
		return success;
	}
	
	private void watchObjectProperty(IPropertyChangedNotify value, final String property)
	{	
		IPropertyChangedListener listener = new IPropertyChangedListener() {			
			@Override
			public void onPropertyChanged(String changedProperty, Object newValue,Object oldValue) {
				if(property.equals(changedProperty))
				{
					fireWatchPropertyChanged();
				}
			}
		};
		value.addPropertyChangedListener(listener);
		watchListeners.put(value, listener);
	}
	
	private void fireWatchPropertyChanged()
	{
		Object result = BindingUitl.getPathValue(getObject(), path);
		for(IChangeWatcherListener listener : listeners)
		{
			listener.onChanged(result);
		}
	}
	
	public void addChangeWatcherListener(IChangeWatcherListener listener)
	{
		if(listeners.contains(listener))
		{
			return;
		}
		listeners.add(listener);
	}
	
	public void stop()
	{	
		for (Map.Entry<IPropertyChangedNotify, IPropertyChangedListener> entry : watchListeners.entrySet()) 
		{
			entry.getKey().removePropertyChangedListener(entry.getValue());
		}
		watchListeners.clear();
		listeners.clear();
	}
	
	private Object getObject()
	{
		return source != null ? source : ref.get();
	}
}
